package com.sc.nft.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.sc.nft.dao.*;
import com.sc.nft.entity.*;
import com.sc.nft.entity.dto.AddBoxConditionDTO;
import com.sc.nft.entity.dto.QueryBoxConditionDTO;
import com.sc.nft.entity.req.EcologyCastGasRequest;
import com.sc.nft.entity.vo.*;
import com.sc.nft.entity.vo.ecology.EcologyCastGasVO;
import com.sc.nft.enums.*;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.service.BoxTakeConversionConditionService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.stream.Collectors;

/**
 * 盲盒兑换条件(BoxTakeConversionCondition)表服务实现类
 *
 * @author 王伟成
 * @since 2023-11-07 16:54:54
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class BoxTakeConversionConditionServiceImpl implements BoxTakeConversionConditionService {


    private final BoxTakeConversionConditionDao boxTakeConversionConditionDao;
    private final DigitalCollectionDao digitalCollectionDao;
    private final EquityPropsDao equityPropsDao;
    private final UserCollectionDao userCollectionDao;
    private final UserEquityPropsDao userEquityPropsDao;
    private final UserAssetsDao userAssetsDao;
    private final BoxTakePoolDao boxTakePoolDao;
    private final EcologyPoolDao ecologyPoolDao;
    private final UserWalletDao userWalletDao;

    /**
     * 该功能为改版优化功能接口
     * 执行逻辑 ↓
     * 1.根据盲盒商品id删除盲盒兑换条件
     * 2.新增盲盒兑换条件
     * <p>
     * 验证条件 ↓
     * 1.商品必须为是盲盒
     * 2.兑换条件中,type为1的时候,tripartiteId必须为藏品id
     * 3.如果type为2的时候,在conversionType必须存在可消耗数量的条件并且>0
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addBoxCondition(Boolean addBoxTaskPool, Long boxId, Integer type, Long productBoxId, Integer upperLimit, AddBoxConditionCollectionDTO addBoxConditionCollectionDTO, List<AddBoxConditionDTO> addBoxConditionDTOList, BoxTakePoolConvertScenarioEnum convertScenario, Long daoId, EcologyCastGasRequest ecologyCastGas) {
        if (CollUtil.isEmpty(addBoxConditionDTOList)) {
            log.error("新增盲盒兑换条件,参数为空");
            throw new GlobalRunTimeException("新增盲盒兑换条件,参数为空");
        }
        //DAO内兑换时，品牌id不能为空
        if (Objects.equals(convertScenario, BoxTakePoolConvertScenarioEnum.DAO_CONVERT)) {
            if (Objects.isNull(daoId)) {
                throw new GlobalRunTimeException("品牌ID不能为空");
            }
        }
        //前置判定
        preVerification(addBoxTaskPool, boxId, type, productBoxId, upperLimit, addBoxConditionCollectionDTO, addBoxConditionDTOList, convertScenario, daoId, ecologyCastGas);
        //endregion
        // 判定
        verification(addBoxConditionDTOList, convertScenario);

        // 根据盲盒商品id删除盲盒兑换条件
        removeByProductBoxId(addBoxConditionDTOList.get(0));

        // 新增盲盒兑换条件
        exchangeIntoTreasury(addBoxConditionDTOList, convertScenario, daoId);

    }

    @Override
    public List<QueryBoxConditionVO> queryBoxCondition(QueryBoxConditionDTO queryBoxConditionDTO) {
        // 获取对应的盲盒兑换条件
        return boxTakeConversionConditionDao.queryBoxCondition(queryBoxConditionDTO.getBoxId());
    }

    /**
     * 验证用户是否满足有nft和对应的权益道具,在兑换的时候需要验证
     *
     * @param userId 用户id
     * @param boxId  商品id
     * @param sum    数量
     * @return
     */
    @Override
    public WhetherUserMeetsRedemptionConditionsVO whetherUserMeetsRedemptionConditions(Long userId, Long boxId, Integer sum) {
        // 判定这个兑换品有没有下架.下架的话就抛异常
        BoxTakePool productBoxIdAndStatus = boxTakePoolDao.getIdAndStatus(boxId, true);
        if (BeanUtil.isEmpty(productBoxIdAndStatus)) {
            throw new GlobalRunTimeException("该兑换品已下架");
        }
        QueryBoxConditionDTO queryBoxConditionDTO = new QueryBoxConditionDTO();
        queryBoxConditionDTO.setBoxId(boxId);

        WhetherUserMeetsRedemptionConditionsVO whetherUserMeetsRedemptionConditions = new WhetherUserMeetsRedemptionConditionsVO();
        whetherUserMeetsRedemptionConditions.setSum(sum);


        // 获取兑换条件
        List<QueryBoxConditionVO> queryBoxConditionVOS = queryBoxCondition(queryBoxConditionDTO);
        whetherUserMeetsRedemptionConditions.setQueryBoxConditions(queryBoxConditionVOS);


        // 获取用户的兑换条件对应的物品
        // 获取用户的碎片
        if (whetherUserMeetsRedemptionConditions.whetherFragmentsAreNeeded()) {
            UserAssets userAssets = userAssetsDao.getByUserIdAndCoinId(userId, CoinCategoryEnum.INTEGRAL.getCode());
            whetherUserMeetsRedemptionConditions.setUserAssets(userAssets);
        }


        // 获取用户的权益道具
        if (whetherUserMeetsRedemptionConditions.whetherEquityPropsAreNeeded()) {
            List<UserEquityProps> userIdAndPropIdsAndTypeEnumAndStatusEnum = userEquityPropsDao.getUserIdAndPropIdsAndTypeEnumAndStatusEnum(userId, whetherUserMeetsRedemptionConditions.getEquityPropsIds(), UserEquityPropsHoldingStatusEnum.NORMAL, EquityPropsTypeEnum.VIRTUAL_PROPS, EquityPropsTypeEnum.VOUCHER, EquityPropsTypeEnum.RAFFLE_TICKET);
            whetherUserMeetsRedemptionConditions.setUserEquityProps(userIdAndPropIdsAndTypeEnumAndStatusEnum);
        }

        // 获取用户的NFT持有
        QueryBoxConditionVO collectionResult = whetherUserMeetsRedemptionConditions.getCollectionResult();
        if (BeanUtil.isNotEmpty(collectionResult)) {
            List<UserCollection> userCollections = userCollectionDao.getByCollectionIdAndUserId(collectionResult.getTripartiteId(), userId);
            whetherUserMeetsRedemptionConditions.setCollections(userCollections);
        }

        //生态积分
        if (whetherUserMeetsRedemptionConditions.whetherEcologyPoolPointsNeeded()) {
            if (Objects.isNull(productBoxIdAndStatus.getDaoId())) {
                throw new GlobalRunTimeException("兑换积分时活动所属品牌不能为空");
            }
            EcologyPool ecologyPool = ecologyPoolDao.getOneByTypeAndDaoIdUserId(EcologyPoolTypeEnum.ACCOUNT, EcologyPoolLinkTypeEnum.POINTS, productBoxIdAndStatus.getDaoId(), userId);
            whetherUserMeetsRedemptionConditions.setEcologyPool(ecologyPool);
        }

        // 对比是否可以兑换.这里面要考虑数量可以兑换几个 返回结果
        return whetherUserMeetsRedemptionConditions;
    }

    /**
     * 扣除用户兑换数据.
     *
     * @param userId
     * @param productBoxId
     * @param sum
     * @return
     */
    @Override
    public Boolean deductUserRedemptionConfiguration(Long userId, Long productBoxId, Integer sum) {

        return null;
    }


    @Override
    public BoxTakeConversionConditionCombination boxTakeConversionConditionCombination(Long boxId) {
        BoxTakeConversionConditionCombination boxTakeConversionConditionCombination = new BoxTakeConversionConditionCombination();
        List<QueryBoxConditionVO> queryBoxConditions = boxTakeConversionConditionDao.queryBoxCondition(boxId);
        // queryBoxConditions使用Java8特性根据type进行分组处理
        Map<Integer, List<QueryBoxConditionVO>> groupCombination =
                queryBoxConditions.stream().collect(Collectors.groupingBy(QueryBoxConditionVO::getType));
        StringJoiner collection = new StringJoiner(",");
        StringJoiner boxCondition = new StringJoiner(",");
        groupCombination.forEach((k, v) -> {
            if (k.equals(1)) {
                v.forEach(queryBoxConditionVO -> {
                    collection.add(queryBoxConditionVO.getName());
                });
            }
            if (k.equals(2)) {
                v.forEach(queryBoxConditionVO -> {
                    boxCondition.add(StrUtil.format("{}*{}", queryBoxConditionVO.getName(), queryBoxConditionVO.getCostQuantity()));
                });
            }
        });
        boxTakeConversionConditionCombination.setCollectionName(collection.toString());
        boxTakeConversionConditionCombination.setConditionName(boxCondition.toString());
        return boxTakeConversionConditionCombination;
    }

    @Override
    public QueryWebBoxConditionPackagingVO queryConversionConditions(QueryBoxConditionDTO queryBoxConditionDTO, UserInfo user) {
        /*
         * 获取该盲盒兑换所需要的条件
         * 获取用户所拥有的权益道具-星球碎片-nft藏品
         * 把所有的条件进行组合并且进行处理判定进行组合.
         *
         * 一共要获取三种数据  1.关于nft的  2.用户的积分碎片的 3.用户的权益道具
         * */
        // 获取条件
        List<QueryBoxConditionVO> queryBoxConditionVOS = queryBoxCondition(queryBoxConditionDTO);

        // 返回参数
        QueryWebBoxConditionPackagingVO queryWebBoxConditionPackagingVO = new QueryWebBoxConditionPackagingVO();
        queryWebBoxConditionPackagingVO.setQueryBoxCondition(BeanUtil.copyToList(queryBoxConditionVOS, QueryWebBoxConditionVO.class));

        // 所需藏品的数据
        QueryWebBoxConditionVO collectionResult = queryWebBoxConditionPackagingVO.getCollectionResult();

        if (BeanUtil.isNotEmpty(collectionResult)) {
            // 通过藏品id和用户id获取用户所拥有的藏品
            List<UserCollection> userCollections = userCollectionDao.getByCollectionIdAndUserId(collectionResult.getTripartiteId(), user.getId());
            // 处理匹配nft的数据.
            queryWebBoxConditionPackagingVO.nftConfiguration(userCollections);
        }
        // 获取用户星球碎片
        UserAssets userAssets = userAssetsDao.getByUserIdAndCoinId(user.getId(), CoinCategoryEnum.INTEGRAL.getCode());
        if (BeanUtil.isNotEmpty(userAssets)) {
            // 转换星球碎片结果
            queryWebBoxConditionPackagingVO.planetFragmentsResultInConversion(userAssets);
        }

        // 获取用户权益道具.
        List<UserEquityProps> userIdAndPropIdsAndTypeEnumAndStatusEnum = userEquityPropsDao.getUserIdAndPropIdsAndTypeEnumAndStatusEnum(user.getId(),
                queryWebBoxConditionPackagingVO.getEquityPropsIds(), UserEquityPropsHoldingStatusEnum.NORMAL, EquityPropsTypeEnum.VIRTUAL_PROPS,
                EquityPropsTypeEnum.VOUCHER, EquityPropsTypeEnum.RAFFLE_TICKET);
        queryWebBoxConditionPackagingVO.handleTheAllocationOfEquityProps(userIdAndPropIdsAndTypeEnumAndStatusEnum);

        //生态积分
        BoxTakePool boxTakePool = boxTakePoolDao.getById(queryBoxConditionDTO.getBoxId());
        EcologyPool ecologyPool = (Objects.nonNull(boxTakePool) && Objects.equals(boxTakePool.getConvertScenario(), BoxTakePoolConvertScenarioEnum.DAO_CONVERT) && Objects.nonNull(boxTakePool.getDaoId())) ?
                ecologyPoolDao.getOneByTypeAndDaoIdUserId(EcologyPoolTypeEnum.ACCOUNT, EcologyPoolLinkTypeEnum.POINTS, boxTakePool.getDaoId(), user.getId()) : null;
        queryWebBoxConditionPackagingVO.handleEcologyPoolConvert(ecologyPool);

        // 处理结果集中nft的部分数据.把关于nft的数据清理掉.
        queryWebBoxConditionPackagingVO.removeNftData();
        EcologyCastGasVO ecologyCastGasVO = new EcologyCastGasVO();
        ecologyCastGasVO.setGasConf(boxTakePool.getGasConf());
        ecologyCastGasVO.setConsumeCost(boxTakePool.getConsumeCost());
        ecologyCastGasVO.setConsumeType(boxTakePool.getConsumeType());
        UserWallet userWallet = userWalletDao.getByUserId(user.getId());
        ecologyCastGasVO.setBalance(Objects.isNull(userWallet) ? BigDecimal.ZERO : userWallet.getBalance());
        queryWebBoxConditionPackagingVO.setEcologyCastGas(ecologyCastGasVO);
        return queryWebBoxConditionPackagingVO;
    }

    /**
     * 前置判定
     *
     * @param addBoxTaskPool
     * @param type
     * @param productBoxId
     * @param upperLimit
     * @param addBoxConditionCollectionDTO
     * @param addBoxConditionDTOList
     * @param convertScenario
     * @param daoId
     * @return
     */
    private void preVerification(Boolean addBoxTaskPool, Long boxId, Integer type, Long productBoxId, Integer upperLimit, AddBoxConditionCollectionDTO addBoxConditionCollectionDTO, List<AddBoxConditionDTO> addBoxConditionDTOList, BoxTakePoolConvertScenarioEnum convertScenario, Long daoId, EcologyCastGasRequest ecologyCastGas) {
        if (addBoxTaskPool) {
            BoxTakePool takePool = new BoxTakePool();
            takePool.setDaoId(Objects.equals(convertScenario, BoxTakePoolConvertScenarioEnum.DAO_CONVERT) ? daoId : null);
            takePool.setConvertScenario(convertScenario);
            takePool.setProductBoxId(productBoxId);
            takePool.setStatus(false);
            takePool.setType(type);
            takePool.setUpperLimit(upperLimit);
            if (Objects.nonNull(ecologyCastGas)) {
                takePool.setGasConf(ecologyCastGas.getGasConf());
                if (ecologyCastGas.getGasConf()) {
                    takePool.setConsumeType(ecologyCastGas.getConsumeType());
                    takePool.setConsumeCost(ecologyCastGas.getConsumeCost());
                }
            }
            takePool.insert();
            boxId = takePool.getId();
        } else {
            if (Objects.isNull(boxId)) {
                throw new GlobalRunTimeException("NFT或盲盒不能为空");
            }
            BoxTakePool productBoxIdAndStatus = boxTakePoolDao.getIdAndStatus(boxId, Boolean.FALSE);
            if (BeanUtil.isEmpty(productBoxIdAndStatus)) {
                throw new GlobalRunTimeException("NFT或盲盒不存在或未下架");
            }
            Boolean requireUpdate = false;
            if (Objects.nonNull(upperLimit) && !Objects.equals(upperLimit, productBoxIdAndStatus.getUpperLimit())) {
                productBoxIdAndStatus.setUpperLimit(upperLimit);
                requireUpdate = true;
            }
            if (Objects.equals(convertScenario, BoxTakePoolConvertScenarioEnum.DAO_CONVERT) && !Objects.equals(daoId, productBoxIdAndStatus.getDaoId())) {
                productBoxIdAndStatus.setDaoId(daoId);
                requireUpdate = true;
            }
            if (requireUpdate) {
                productBoxIdAndStatus.updateById();
            }
        }
        if (Objects.nonNull(addBoxConditionCollectionDTO)) {
            if (boxId > 0 && Objects.nonNull(addBoxConditionCollectionDTO.getTripartiteId()) && addBoxConditionCollectionDTO.getTripartiteId() > 0) {
                AddBoxConditionDTO addBoxConditionDTO = new AddBoxConditionDTO();
                addBoxConditionDTO.setBoxId(boxId);
                addBoxConditionDTO.setType(1);
                addBoxConditionDTO.setTripartiteId(addBoxConditionCollectionDTO.getTripartiteId());
                addBoxConditionDTOList.add(addBoxConditionDTO);
            }
        }
        for (AddBoxConditionDTO addBoxConditionDTO : addBoxConditionDTOList) {
            addBoxConditionDTO.setBoxId(boxId);
        }
    }

    private static void verification(List<AddBoxConditionDTO> addBoxConditionDTO, BoxTakePoolConvertScenarioEnum convertScenario) {
        addBoxConditionDTO.forEach(verification -> {
            if (verification.getType().equals(2)) {
                if (Objects.equals(convertScenario, BoxTakePoolConvertScenarioEnum.TRIBE_CONVERT)
                        && verification.getConversionType().equals(BoxConditionEnum.ECOLOGY_POOL_POINTS)) {
                    throw new GlobalRunTimeException("部落兑换不支持生态积分");
                }
                if (Objects.isNull(verification.getConversionType())) {
                    throw new GlobalRunTimeException("兑换方式不能为空");
                }
                if (verification.getConversionType().equals(BoxConditionEnum.STAR_FRAGMENT)
                        || verification.getConversionType().equals(BoxConditionEnum.ECOLOGY_POOL_POINTS)) {
                    if (verification.getCostQuantity() == null || verification.getCostQuantity().equals("") || Convert.toBigDecimal(verification.getCostQuantity()).compareTo(BigDecimal.ZERO) <= 0) {
                        log.info("新增盲盒兑换条件,参数错误.参数为:{}", verification.getCostQuantity());
                        throw new GlobalRunTimeException("新增盲盒兑换条件,参数错误");
                    }
                }
            }
        });
    }

    /**
     * 新增盲盒兑换条件
     *
     * @param addBoxConditionDTO
     */
    private void exchangeIntoTreasury(List<AddBoxConditionDTO> addBoxConditionDTO, BoxTakePoolConvertScenarioEnum convertScenario, Long daoId) {
        // 根据type进行分组.不同的组查询不同的表获取名字以及是否存在对应的权益道具
        addBoxConditionDTO.stream().forEach(addBoxCondition -> {
            if (addBoxCondition.getType().equals(1)) {
                // 根据藏品id查询藏品名字
                DigitalCollection digitalCollection = digitalCollectionDao.getById(addBoxCondition.getTripartiteId());
                if (BeanUtil.isNotEmpty(digitalCollection)) {
                    addBoxCondition.setName(digitalCollection.getFirstTitle());
                    addBoxCondition.setCreationContent(digitalCollection.getCreationContent());
                } else {
                    // 不存在直接抛出异常
                    throw new GlobalRunTimeException("绑定的nft藏品不存在");
                }
            }


            if (addBoxCondition.getType().equals(2)) {
                // 根据权益道具id查询权益道具名字
                if (addBoxCondition.getConversionType().equals(2)) {
                    // 必须是权益道具才可以验证,否则直接跳过
                    EquityProps equityProps = equityPropsDao.getById(addBoxCondition.getTripartiteId());
                    if (BeanUtil.isEmpty(equityProps)) {
                        throw new GlobalRunTimeException("绑定的权益道具不存在");
                    }
                    if (equityProps.getTypeClassify() != EquityPropsTypeEnum.VIRTUAL_PROPS) {
                        throw new GlobalRunTimeException("虚拟道具类型不正确");
                    }
                    addBoxCondition.setName(equityProps.getFirstTitle());
                    addBoxCondition.setCreationContent(equityProps.getCreationContent());
                }
            }
        });
        // 新增盲盒兑换条件
        boxTakeConversionConditionDao.saveBatch(BeanUtil.copyToList(addBoxConditionDTO, BoxTakeConversionCondition.class));
    }

    /**
     * 根据盲盒商品id删除盲盒兑换条件
     *
     * @param addBoxConditionDTO
     */
    private void removeByProductBoxId(AddBoxConditionDTO addBoxConditionDTO) {
        boxTakeConversionConditionDao.removeByProductBoxId(addBoxConditionDTO.getBoxId());
    }
}
